/*
server script has to reponse by json like this:
{
	query: "sent query",
	list: [
		{
			text: "text",
			url: "url"
		},{...}
	]
}
*/
function AutoComplete(){}
AutoComplete.prototype = {
	queryAlias: "query",
	listAlias: "list",
	textAlias: "text",
	urlAlias: "url",
	queryUrl: "query.php",
	maxHeight: 150,
	delay: 120,
	init: function(input,opts) {
		opts = opts || {};
		this.input = input;
		this.queryUrl = opts.url || this.queryUrl;	//default url
		this.maxHeight = opts.maxHeight || this.maxHeight;
		this.delay = opts.delay || this.delay;
		this.cache = {};
		this.keyPressTimeout = null;
		
		this.addEvents();
	},
	addEvents: function() {
		as.w(this.input).keyup(this.setDelay,this).focus(this.checkDelay,this).blur(this.tryClearACList,this).keypress(this.preventSend,this);
	},
	preventSend: function(e) {
		if (this.ACListWrapper && e.keyCode == 13) e.preventDefault();
	},
	cacheResult: function(response) {
		this.cache[response[this.queryAlias]] = response;
	},
	setDelay: function(e) {
		if (e.keyCode == 13) {
			this.selectItemByKeyPress();
		}
		else if (e.keyCode == 27) {
			this.clearACList();
		}
		else if (e.keyCode == 38 && this.ACListWrapper) {
			this.walkACListUp();
		}
		else if (e.keyCode == 40 && this.ACListWrapper) {
			this.walkACListDown();
		}
		else if (e.keyCode == 37 || e.keyCode == 39) return;
		
		else {
			clearTimeout(this.keyPressTimeout);
			this.keyPressTimeout = setTimeout(as.bind(this.checkDelay,this),this.delay);
		}
	},
	checkDelay: function(e) {
		if (this.input.value == "") {
			this.clearACList();
			return;
		}
		this.cache[this.input.value] ? this.createAC(this.cache[this.input.value]) : as.ajax(this.queryUrl+"?query="+encodeURIComponent(this.input.value),as.bind(this.getACResponse,this));
	},
	getACResponse: function(response) {
		try {
			response = eval("("+response+")");
			this.createAC(response);
		}
		catch(e) {}
	},
	createAC: function(response) {
		this.clearACList();
		if (this.input.value == "") return;
		var list = response[this.listAlias];
		this.ACListWrapper = as.create("<div class='as-autocomplete-wrapper'><iframe frameborder='0'></iframe>");
		this.ACList = as.append("ul",this.ACListWrapper);
		var replaceRegexp = new RegExp("(^| )(" + response[this.queryAlias] + ")","gi");
		as.foreach(list,as.bind(function(item){
			as.append("<li><a href='"+item[this.urlAlias]+"'>" + 
				item[this.textAlias].replace(replaceRegexp,"<strong>$2</strong>") + 
			"</a></li>",this.ACList);
		},this));
		this.ACItems = as.$("a",this.ACListWrapper);
		as.w("a",this.ACListWrapper).click(this.selectItemByClick,this);
		this.cacheResult(response);
		this.showACList();
	},
	showACList: function() {
		var inputCS = as.getElementPosition(this.input);
		as.append(this.ACListWrapper,document.body);
		as.style(this.ACListWrapper, {
			left: inputCS.left + "px",
			top: inputCS.top + inputCS.height + 1 + "px",
			width: inputCS.width - 2 + "px"
		});
		if (this.ACList.scrollHeight > this.maxHeight) {
			this.ACList.style.height = this.maxHeight + "px"
		}
	},
	walkACListDown: function() {
		var hoveredACItem;
		if (this.hoveredACItem == null || this.hoveredACItem == undefined) {
			hoveredACItem = 0;
		}
		else {		
			hoveredACItem = (this.hoveredACItem == this.ACItems.length - 1) ? 0 : this.hoveredACItem + 1;
		}
		this.hoverACItem(hoveredACItem);
	},
	walkACListUp: function() {
		var hoveredACItem;
		if (this.hoveredACItem == null || this.hoveredACItem == undefined) {
			hoveredACItem = this.ACItems.length - 1;
		}
		else {
			hoveredACItem = (this.hoveredACItem == 0) ? this.ACItems.length - 1 : this.hoveredACItem - 1;
		}
		this.hoverACItem(hoveredACItem);
	},
	scrollToHovered: function() {
		var hoveredCS = as.getElementPosition(this.ACItems[this.hoveredACItem]);
		var listCS = as.getElementPosition(this.ACList);
		var scrollTop = this.ACList.scrollTop;
		
		if (hoveredCS.top + hoveredCS.height > listCS.top + listCS.height) {
			this.ACList.scrollTop = scrollTop + hoveredCS.top + hoveredCS.height - (listCS.top + listCS.height);
		}
		else if (hoveredCS.top < listCS.top) {
			this.ACList.scrollTop = scrollTop + hoveredCS.top - listCS.top;
		}
	},
	hoverACItem: function(hoveredACItem) {
		this.clearFormerHovered();
		this.hoveredACItem = hoveredACItem;
		this.ACItems[this.hoveredACItem].className += " hover";
		this.scrollToHovered();
		this.input.value = this.ACItems[this.hoveredACItem].innerHTML.replace(/<\\?[^>]+>/gi,"");
	},
	selectItemByKeyPress: function() {
		(this.hoveredACItem != null) && (this.hoveredACItem != undefined) && this.selectItem(this.ACItems[this.hoveredACItem]);
	},
	selectItemByClick: function(e) {
		e.preventDefault();
		this.selectItem(as.is(e.target,"a") ? e.target : as.parent(e.target,"a"));
	},
	selectItem: function(selectedItem) {
		this.input.value = selectedItem.innerHTML.replace(/<\\?[^>]+>/gi,"");
		this.clearACList();
	},
	clearFormerHovered: function() {
		var formerHovered = this.ACItems[this.hoveredACItem];
		if (!formerHovered) return;
		formerHovered.className = formerHovered.className.replace(/\bhover\b/gi,"");
	},
	tryClearACList: function(e) {
		setTimeout(as.bind(this.clearACList,this),300);
	},
	clearACList: function() {
		as.remove(this.ACListWrapper);
		this.ACListWrapper = null;
		this.hoveredACItem = null;
	},
	cacheResult: function(response) {
		this.cache[response[this.queryAlias]] = response;
	}
}
AutoComplete = as.Plugin("autocomplete",AutoComplete);